#include "stdafx.h"
#include "Demo.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define STEP  0.1f
#define ANGLE 0.2f

#include <string>
#include "GL/glew.h"
#include "GL/freeglut.h"
#include "GL/freeglut_ext.h"
#include "SmartPtr.h"
#include "Scene.h"
#include "IRenderer.h"
#include "SWRenderer.h"
#include "MeshModel.h"

using std::string;
using std::ifstream;

#define BUFFER_OFFSET( offset )   ((GLvoid*) (offset))

#define FILE_OPEN 1
#define MAIN_ABOUT 2
#define MOUSESENSITIVITY 6

RendererPtr renderer;
ScenePtr scene;

CvarPtr _width;
CvarPtr _height;
CvarPtr _draw_grid;
CvarPtr _draw_axis;
CvarPtr _draw_bounding_box;
CvarPtr _draw_vertex_normals;
CvarPtr _draw_face_normals;
CvarPtr _active_model;

int models = 0;

int last_x,last_y;
bool lb_down,rb_down,mb_down;

void redraw()
{
	renderer->startScene();
	scene->render(renderer);
	renderer->endScene();
}

//----------------------------------------------------------------------------
// Callbacks

void display( void )
{
	//Call the scene and ask it to draw itself
}

void reshape( int width, int height )
{
	_width->setValue(width);
	_height->setValue(height);

	renderer->resize();

	redraw();
}

void keyboard( unsigned char key, int x, int y )
{
	switch ( key ) {
	case 'w': // forward
		scene->getActiveCamera()->move(0.0f, 0.0f, -STEP);		
		break;
	case 's': // backward
		scene->getActiveCamera()->move(0.0f, 0.0f, STEP);
		break;	
	case 'a': // left
		scene->getActiveCamera()->move(-STEP, 0.0f, 0.0f);		
		break;
	case 'd': // right
		scene->getActiveCamera()->move(STEP, 0.0f, 0.0f);
		break;
	case 'q': // down
		scene->getActiveCamera()->move(0.0f, -STEP, 0.0f);
		break;
	case 'e': // up
		scene->getActiveCamera()->move(0.0f, STEP, 0.0f);
		break;	
	case 'W': // forward
		scene->getActiveModel()->translate(0.0f, 0.0f, -STEP);		
		break;
	case 'S': // backward
		scene->getActiveModel()->translate(0.0f, 0.0f, STEP);
		break;	
	case 'A': // left
		scene->getActiveModel()->translate(-STEP, 0.0f, 0.0f);		
		break;
	case 'D': // right
		scene->getActiveModel()->translate(STEP, 0.0f, 0.0f);
		break;
	case 'Q': // down
		scene->getActiveModel()->translate(0.0f, -STEP, 0.0f);
		break;
	case 'E': // up
		scene->getActiveModel()->translate(0.0f, STEP, 0.0f);
		break;	
	/*
	case 'W': // taller
		scene->getActiveModel()->scale(1.0f, 1.1f, 1.0f);
		break;
	case 'S': // shorter
		scene->getActiveModel()->scale(1.0f, 0.9f, 1.0f);
		break;	
	case 'A': // wider
		scene->getActiveModel()->scale(1.1f, 1.0f, 1.0f);		
		break;
	case 'D': // nerower
		scene->getActiveModel()->scale(0.9f, 1.0f, 1.0f);
		break;
	case 'Q': // deeper
		scene->getActiveModel()->scale(1.0f, 1.0f, 1.1f);
		break;
	case 'E': // less deep
		scene->getActiveModel()->scale(1.0f, 1.0f, 0.9f);
		break;
	*/
	case '<':
		scene->getActiveCamera()->rotate(0.0f, 0.0f, ANGLE); // roll left
		break;
	case '>':
		scene->getActiveCamera()->rotate(0.0f, 0.0f, -ANGLE); // roll right
		break;
	case 033:
		exit( EXIT_SUCCESS );
		break;
	}

	redraw();
}

void keyboardSpecial(int key, int x, int y)
{
	switch ( key ) {
	case GLUT_KEY_F1: // toggle axis
		_draw_axis->setValue(!_draw_axis->getValueB());
		_draw_grid->setValue(!_draw_grid->getValueB());
		break;
	case GLUT_KEY_F2: // toggle bounding box
		_draw_bounding_box->setValue(!_draw_bounding_box->getValueB());
		break;
	case GLUT_KEY_F3: // toggle face normals
		_draw_face_normals->setValue(!_draw_face_normals->getValueB());
		break;
	case GLUT_KEY_F4: // toggle Vertex normals
		_draw_vertex_normals->setValue(!_draw_vertex_normals->getValueB());
		break;
	case GLUT_KEY_LEFT:
		scene->getActiveCamera()->rotate(0.0f, -ANGLE, 0.0f); // rotate left
		break;
	case GLUT_KEY_RIGHT:
		scene->getActiveCamera()->rotate(0.0f, ANGLE, 0.0f); // rotate right
		break;	
	case GLUT_KEY_UP:
		scene->getActiveCamera()->rotate(ANGLE, 0.0f, 0.0f); // rotate u	p
		break;
	case GLUT_KEY_DOWN:
		scene->getActiveCamera()->rotate(-ANGLE, 0.0f, 0.0f); // rotate down
		break;
	}

	redraw();
}

void mouse(int button, int state, int x, int y)
{
	//button = {GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON, GLUT_RIGHT_BUTTON}
	//state = {GLUT_DOWN,GLUT_UP}

	//set down flags
	switch(button) {
	case GLUT_LEFT_BUTTON:
		lb_down = (state==GLUT_UP)?0:1;
		break;
	case GLUT_RIGHT_BUTTON:
		rb_down = (state==GLUT_UP)?0:1;
		break;
	case GLUT_MIDDLE_BUTTON:
		mb_down = (state==GLUT_UP)?0:1;	
		break;
	}

	last_x=x;
	last_y=y;
}

void motion(int x, int y)
{
	// calc difference in mouse movement
	int dx = x-last_x;
	int dy = y-last_y;
	// update last x,y
	last_x=x;
	last_y=y;

	scene->getActiveCamera()->rotate(ANGLE*dy*MOUSESENSITIVITY, 0.0f, 0.0f);
	scene->getActiveCamera()->rotate(0.0f, ANGLE*dx*MOUSESENSITIVITY, 0.0f);
	
	redraw();
}

void fileMenu(int id)
{
	switch (id)
	{
	case FILE_OPEN:
		CFileDialog dlg(TRUE,_T(".obj"),NULL,NULL,_T("*.obj|*.*"));
		if(dlg.DoModal()==IDOK)
		{
			SmartPtr<Model> model(new MeshModel((LPCTSTR)dlg.GetPathName()));
			scene->addModel(model);
			_active_model->setValue(models++);
			redraw();
		}
		break;
	}
}

void mainMenu(int id)
{
	switch (id)
	{
	case MAIN_ABOUT:
		AfxMessageBox(_T("Computer Graphics"));
		break;
	}
}

void initMenu()
{
	int menuFile = glutCreateMenu(fileMenu);
	glutAddMenuEntry("Open..",FILE_OPEN);
	glutCreateMenu(mainMenu);
	glutAddSubMenu("File",menuFile);
	glutAddMenuEntry("About",MAIN_ABOUT);
	glutAttachMenu(GLUT_RIGHT_BUTTON);
}
//----------------------------------------------------------------------------



int my_main( int argc, char **argv )
{
	//----------------------------------------------------------------------------
	// Initialize window
	glutInit( &argc, argv );
	glutInitDisplayMode( GLUT_RGBA| GLUT_DOUBLE);
	glutInitWindowSize( 512, 512 );
	glutInitContextVersion( 3, 2 );
	glutInitContextProfile( GLUT_CORE_PROFILE );
	glutCreateWindow( "CG" );
	
	glewExperimental = GL_TRUE;
	glewInit();
	GLenum err = glewInit();

	if (GLEW_OK != err) {
		/* Problem: glewInit failed, something is seriously wrong. */
		fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
		/*		...*/
	}
	fprintf(stdout, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));

	renderer = RendererPtr(new SWRenderer());
	scene = ScenePtr(new Scene());

	CvarSystem& cvars = CvarSystem::getInstance();
	_width = cvars.getCvar("r_width");
	_height = cvars.getCvar("r_height");
	_draw_axis = cvars.getCvar("s_axis", true);
	_draw_grid = cvars.getCvar("s_grid", true);
	_draw_bounding_box = cvars.getCvar("m_bounding_box", true);
	_draw_face_normals = cvars.getCvar("m_face_normals", true);
	_draw_vertex_normals = cvars.getCvar("m_vertex_normals", true);
	_active_model = cvars.getCvar("s_active_model");

	cvars.getCvar("r_use_glut")->setValue(true);

	//----------------------------------------------------------------------------
	// Initialize Callbacks

	glutDisplayFunc( display );
	glutKeyboardFunc( keyboard );
	glutSpecialFunc( keyboardSpecial );
	glutMouseFunc( mouse );
	glutMotionFunc ( motion );
	glutReshapeFunc( reshape );
	initMenu();

	glutMainLoop();

	return 0;
}

CWinApp theApp;

int main( int argc, char **argv )
{
	int nRetCode = 0;

	// initialize MFC and print and error on failure
	if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) {
		// TODO: change error code to suit your needs
		_tprintf(_T("Fatal Error: MFC initialization failed\n"));
		nRetCode = 1;
	}
	else {
		my_main(argc, argv );
	}

	return nRetCode;
}
